#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctime>
using namespace std;

const int gmaxnum =666;

template <class T>
class BlockQueue
{   
public:
    BlockQueue(const int &maxnum = gmaxnum):_maxnum(maxnum)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_pcond, nullptr);
        pthread_cond_init(&_ccond, nullptr);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_pcond);
        pthread_cond_destroy(&_ccond);
    }

    void push(const T &in) // 输入型参数 &
    {
        pthread_mutex_lock(&_mutex);
    
        // 必须是while 不能用if
        while(is_full()) 
        {
            // pthread_cond_wait
            //调用前必须已锁定mutex
            //函数被调用时 会原子性地释放mutex并使线程进入等待状态
            //被唤醒后会自动重新获取你传入的mutex
            //必须使用while循环检查条件 防止虚假唤醒

            pthread_cond_wait(&_pcond, &_mutex); //容量已满 生产条件不满足 无法生产 生产者进行等待
        }
        // 走到这里 队列一定没有满
        _q.push(in);
        // 此时阻塞队列内 一定有数据

        pthread_cond_signal(&_ccond); //唤醒消费者

        pthread_mutex_unlock(&_mutex);
    }
    void pop(T *out) // 输出型参数 *
    {
        pthread_mutex_lock(&_mutex);
    
        while(is_empty()) 
        {
            pthread_cond_wait(&_ccond, &_mutex); //容量已空 消费条件不满足 无法消费 消费者进行等待
        }
        // 走到这里 队列一定不为空
        *out = _q.front();
        _q.pop();

        // 此时阻塞队列内 至少有一个空的位置
        pthread_cond_signal(&_pcond); // 唤醒生产者
        pthread_mutex_unlock(&_mutex);
    }
    
private:
    bool is_empty()
    {
        return _q.empty();
    }
    bool is_full()
    {
        return _q.size() == _maxnum;
    }
private:
    queue<T> _q;
    int _maxnum; // 队列中元素个数上限
    pthread_mutex_t _mutex;
    pthread_cond_t _pcond; // 生产者对应的条件变量
    pthread_cond_t _ccond; // 消费者对应的条件变量
};